<!DOCTYPE html>
<html lang="zh">

<head>
    <meta charset="UTF-8"/>
<meta http-equiv="X-UA-Compatible" content="IE=edge"/>
<meta name="viewport" content="width=device-width"/>

<meta name="theme-color" content="#414852"/>


<link rel="stylesheet" href="https://fonts.googleapis.com/css?family=Inconsolata|Itim|Lobster.css">
<link rel="icon" type="image/png" sizes="64x64" href="/images/favicon/xin-64.png" /><link rel="icon" type="image/png" sizes="128x128" href="/images/favicon/xin-128.png" /><link rel="apple-touch-icon" sizes="128x128" href="/images/favicon/xin-128.png" /><link rel="mask-icon" color="white" href="/images/favicon/xin.svg" />  <meta name="description" content="众所周知，netflix OSS 2.0 难产了，上一代的zuul网关虽说不错，但其并不是异步的。所以，Spring团队推出了基于Spring Webflux的全新异步的网关–Spring Cloud Gateway。  本文内容基于Spring Cloud Gateway 2.1.0.GA  来跟着我一步步，探索它的魅力坑吧！">
<meta property="og:type" content="article">
<meta property="og:title" content="Spring Cloud 之 Gateway （Greenwich版）">
<meta property="og:url" content="https://jiangtj.com/articles/beechnut/spring-cloud-gateway/index.html">
<meta property="og:site_name" content="Dnocm ☀">
<meta property="og:description" content="众所周知，netflix OSS 2.0 难产了，上一代的zuul网关虽说不错，但其并不是异步的。所以，Spring团队推出了基于Spring Webflux的全新异步的网关–Spring Cloud Gateway。  本文内容基于Spring Cloud Gateway 2.1.0.GA  来跟着我一步步，探索它的魅力坑吧！">
<meta property="og:locale">
<meta property="article:published_time" content="2019-05-20T13:16:33.000Z">
<meta property="article:modified_time" content="2019-05-20T13:16:33.000Z">
<meta property="article:author" content="Mr.J">
<meta property="article:tag" content="Spring Cloud">
<meta property="article:tag" content="微服务">
<meta name="twitter:card" content="summary">  <title>Spring Cloud 之 Gateway （Greenwich版） | Dnocm ☀</title>    <script type="application/ld+json">{"@context":"https://schema.org","@type":"Article","mainEntityOfPage":{"@type":"WebPage","@id":"https://jiangtj.com/articles/beechnut/spring-cloud-gateway/"},"headline":"Spring Cloud 之 Gateway （Greenwich版）","image":[],"datePublished":"2019-05-20T13:16:33.000Z","dateModified":"2019-05-20T13:16:33.000Z","author":{"@type":"Person","name":"Mr.J"},"publisher":{"@type":"Organization","name":"Mr.J","logo":{"@type":"ImageObject","url":"https://jiangtj.com/images/avatar.jpg"}}}</script><script data-ad-client="ca-pub-8243423215749776" async src="https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js"></script>
<meta name="google-site-verification" content="TWf4Ma3V7xRcvKFWw_1iOCJKgNJmUUB1CmLucKjvV2A" />
<meta name="baidu-site-verification" content="xtf2YgRPUK" />

<!-- Global site tag (gtag.js) - Google Analytics -->
<script async src="https://www.googletagmanager.com/gtag/js?id=UA-132230328-1"></script>
<script>
  window.dataLayer = window.dataLayer || [];
  function gtag(){dataLayer.push(arguments);}
  gtag('js', new Date());

  gtag('config', 'UA-132230328-1');
</script>
<link rel="stylesheet" type="text/css" href="/css/injector/main.css" /><link rel="preload" as="style" href="/css/injector/dark.css" /><link rel="preload" as="style" href="/css/injector/light.css" /><link rel="preload" as="style" href="/css/injector/auto.css" /><!-- hexo injector head_end start --><link rel="manifest" href="/manifest.json" /><!-- hexo injector head_end end --><meta name="generator" content="Hexo 5.4.0"><link rel="alternate" href="/rss2.xml" title="Dnocm ☀" type="application/rss+xml">
</head>

<body>

    
  <header class="main-header">
    <div class="header-inner card"><div class="brand">
  <div class="site-meta">
    <div class="custom-logo-site-title">
      <a href="/">
        <span class="site-title">Dnocm ☀</span>
      </a>
    </div>
    
  </div>

  <div class="site-nav-toggle">
    <button>
      <svg aria-hidden="true" focusable="false" data-prefix="fa" data-icon="bars" class="svg-inline--fa fa-bars fa-w-14 icon" role="img" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 448 512"><path fill="currentColor" d="M16 132h416c8.837 0 16-7.163 16-16V76c0-8.837-7.163-16-16-16H16C7.163 60 0 67.163 0 76v40c0 8.837 7.163 16 16 16zm0 160h416c8.837 0 16-7.163 16-16v-40c0-8.837-7.163-16-16-16H16c-8.837 0-16 7.163-16 16v40c0 8.837 7.163 16 16 16zm0 160h416c8.837 0 16-7.163 16-16v-40c0-8.837-7.163-16-16-16H16c-8.837 0-16 7.163-16 16v40c0 8.837 7.163 16 16 16z"></path></svg>    </button>
  </div>
</div>
<nav class="site-nav">
  <ul class="menu">
    <li class="menu-item menu-item-home"><a href="/"><svg aria-hidden="true" focusable="false" data-prefix="fa" data-icon="home" class="svg-inline--fa fa-home fa-w-18 icon" role="img" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512"><path fill="currentColor" d="M280.37 148.26L96 300.11V464a16 16 0 0 0 16 16l112.06-.29a16 16 0 0 0 15.92-16V368a16 16 0 0 1 16-16h64a16 16 0 0 1 16 16v95.64a16 16 0 0 0 16 16.05L464 480a16 16 0 0 0 16-16V300L295.67 148.26a12.19 12.19 0 0 0-15.3 0zM571.6 251.47L488 182.56V44.05a12 12 0 0 0-12-12h-56a12 12 0 0 0-12 12v72.61L318.47 43a48 48 0 0 0-61 0L4.34 251.47a12 12 0 0 0-1.6 16.9l25.5 31A12 12 0 0 0 45.15 301l235.22-193.74a12.19 12.19 0 0 1 15.3 0L530.9 301a12 12 0 0 0 16.9-1.6l25.5-31a12 12 0 0 0-1.7-16.93z"></path></svg>首页</a></li><li class="menu-item menu-item-tags"><a href="/tags/"><svg aria-hidden="true" focusable="false" data-prefix="fa" data-icon="tags" class="svg-inline--fa fa-tags fa-w-20 icon" role="img" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 640 512"><path fill="currentColor" d="M497.941 225.941L286.059 14.059A48 48 0 0 0 252.118 0H48C21.49 0 0 21.49 0 48v204.118a48 48 0 0 0 14.059 33.941l211.882 211.882c18.744 18.745 49.136 18.746 67.882 0l204.118-204.118c18.745-18.745 18.745-49.137 0-67.882zM112 160c-26.51 0-48-21.49-48-48s21.49-48 48-48 48 21.49 48 48-21.49 48-48 48zm513.941 133.823L421.823 497.941c-18.745 18.745-49.137 18.745-67.882 0l-.36-.36L527.64 323.522c16.999-16.999 26.36-39.6 26.36-63.64s-9.362-46.641-26.36-63.64L331.397 0h48.721a48 48 0 0 1 33.941 14.059l211.882 211.882c18.745 18.745 18.745 49.137 0 67.882z"></path></svg>标签</a></li><li class="menu-item menu-item-categories"><a href="/categories/"><svg aria-hidden="true" focusable="false" data-prefix="fa" data-icon="th" class="svg-inline--fa fa-th fa-w-16 icon" role="img" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512"><path fill="currentColor" d="M149.333 56v80c0 13.255-10.745 24-24 24H24c-13.255 0-24-10.745-24-24V56c0-13.255 10.745-24 24-24h101.333c13.255 0 24 10.745 24 24zm181.334 240v-80c0-13.255-10.745-24-24-24H205.333c-13.255 0-24 10.745-24 24v80c0 13.255 10.745 24 24 24h101.333c13.256 0 24.001-10.745 24.001-24zm32-240v80c0 13.255 10.745 24 24 24H488c13.255 0 24-10.745 24-24V56c0-13.255-10.745-24-24-24H386.667c-13.255 0-24 10.745-24 24zm-32 80V56c0-13.255-10.745-24-24-24H205.333c-13.255 0-24 10.745-24 24v80c0 13.255 10.745 24 24 24h101.333c13.256 0 24.001-10.745 24.001-24zm-205.334 56H24c-13.255 0-24 10.745-24 24v80c0 13.255 10.745 24 24 24h101.333c13.255 0 24-10.745 24-24v-80c0-13.255-10.745-24-24-24zM0 376v80c0 13.255 10.745 24 24 24h101.333c13.255 0 24-10.745 24-24v-80c0-13.255-10.745-24-24-24H24c-13.255 0-24 10.745-24 24zm386.667-56H488c13.255 0 24-10.745 24-24v-80c0-13.255-10.745-24-24-24H386.667c-13.255 0-24 10.745-24 24v80c0 13.255 10.745 24 24 24zm0 160H488c13.255 0 24-10.745 24-24v-80c0-13.255-10.745-24-24-24H386.667c-13.255 0-24 10.745-24 24v80c0 13.255 10.745 24 24 24zM181.333 376v80c0 13.255 10.745 24 24 24h101.333c13.255 0 24-10.745 24-24v-80c0-13.255-10.745-24-24-24H205.333c-13.255 0-24 10.745-24 24z"></path></svg>分类</a></li><li class="menu-item menu-item-archives"><a href="/archives/"><svg aria-hidden="true" focusable="false" data-prefix="fa" data-icon="archive" class="svg-inline--fa fa-archive fa-w-16 icon" role="img" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512"><path fill="currentColor" d="M32 448c0 17.7 14.3 32 32 32h384c17.7 0 32-14.3 32-32V160H32v288zm160-212c0-6.6 5.4-12 12-12h104c6.6 0 12 5.4 12 12v8c0 6.6-5.4 12-12 12H204c-6.6 0-12-5.4-12-12v-8zM480 32H32C14.3 32 0 46.3 0 64v48c0 8.8 7.2 16 16 16h480c8.8 0 16-7.2 16-16V64c0-17.7-14.3-32-32-32z"></path></svg>归档</a></li><li class="menu-item menu-item-more"><a href="/more/"><svg aria-hidden="true" focusable="false" data-prefix="fa" data-icon="info-circle" class="svg-inline--fa fa-info-circle fa-w-16 icon" role="img" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512"><path fill="currentColor" d="M256 8C119.043 8 8 119.083 8 256c0 136.997 111.043 248 248 248s248-111.003 248-248C504 119.083 392.957 8 256 8zm0 110c23.196 0 42 18.804 42 42s-18.804 42-42 42-42-18.804-42-42 18.804-42 42-42zm56 254c0 6.627-5.373 12-12 12h-88c-6.627 0-12-5.373-12-12v-24c0-6.627 5.373-12 12-12h12v-64h-12c-6.627 0-12-5.373-12-12v-24c0-6.627 5.373-12 12-12h64c6.627 0 12 5.373 12 12v100h12c6.627 0 12 5.373 12 12v24z"></path></svg>More</a></li><li class="menu-item menu-item-search"><a href="javascript:;" class="popup-trigger"><svg aria-hidden="true" focusable="false" data-prefix="fas" data-icon="search" class="svg-inline--fa fa-search fa-w-16 icon" role="img" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512"><path fill="currentColor" d="M505 442.7L405.3 343c-4.5-4.5-10.6-7-17-7H372c27.6-35.3 44-79.7 44-128C416 93.1 322.9 0 208 0S0 93.1 0 208s93.1 208 208 208c48.3 0 92.7-16.4 128-44v16.3c0 6.4 2.5 12.5 7 17l99.7 99.7c9.4 9.4 24.6 9.4 33.9 0l28.3-28.3c9.4-9.4 9.4-24.6.1-34zM208 336c-70.7 0-128-57.2-128-128 0-70.7 57.2-128 128-128 70.7 0 128 57.2 128 128 0 70.7-57.2 128-128 128z"></path></svg>搜索</a></li>  </ul>
</nav>
</div>
  </header>

  <div class="flex-container">

    

<aside class="sidebar">

  <div class="header-inner card">
    <div class="brand">
  <div class="site-meta">
    <div class="custom-logo-site-title">
      <a href="/">
        <span class="site-title">Dnocm ☀</span>
      </a>
    </div>
    
  </div>

  <div class="site-nav-toggle">
    <button>
      <svg aria-hidden="true" focusable="false" data-prefix="fa" data-icon="bars" class="svg-inline--fa fa-bars fa-w-14 icon" role="img" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 448 512"><path fill="currentColor" d="M16 132h416c8.837 0 16-7.163 16-16V76c0-8.837-7.163-16-16-16H16C7.163 60 0 67.163 0 76v40c0 8.837 7.163 16 16 16zm0 160h416c8.837 0 16-7.163 16-16v-40c0-8.837-7.163-16-16-16H16c-8.837 0-16 7.163-16 16v40c0 8.837 7.163 16 16 16zm0 160h416c8.837 0 16-7.163 16-16v-40c0-8.837-7.163-16-16-16H16c-8.837 0-16 7.163-16 16v40c0 8.837 7.163 16 16 16z"></path></svg>    </button>
  </div>
</div>
<nav class="site-nav">
  <ul class="menu">
    <li class="menu-item menu-item-home"><a href="/"><svg aria-hidden="true" focusable="false" data-prefix="fa" data-icon="home" class="svg-inline--fa fa-home fa-w-18 icon" role="img" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512"><path fill="currentColor" d="M280.37 148.26L96 300.11V464a16 16 0 0 0 16 16l112.06-.29a16 16 0 0 0 15.92-16V368a16 16 0 0 1 16-16h64a16 16 0 0 1 16 16v95.64a16 16 0 0 0 16 16.05L464 480a16 16 0 0 0 16-16V300L295.67 148.26a12.19 12.19 0 0 0-15.3 0zM571.6 251.47L488 182.56V44.05a12 12 0 0 0-12-12h-56a12 12 0 0 0-12 12v72.61L318.47 43a48 48 0 0 0-61 0L4.34 251.47a12 12 0 0 0-1.6 16.9l25.5 31A12 12 0 0 0 45.15 301l235.22-193.74a12.19 12.19 0 0 1 15.3 0L530.9 301a12 12 0 0 0 16.9-1.6l25.5-31a12 12 0 0 0-1.7-16.93z"></path></svg>首页</a></li><li class="menu-item menu-item-tags"><a href="/tags/"><svg aria-hidden="true" focusable="false" data-prefix="fa" data-icon="tags" class="svg-inline--fa fa-tags fa-w-20 icon" role="img" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 640 512"><path fill="currentColor" d="M497.941 225.941L286.059 14.059A48 48 0 0 0 252.118 0H48C21.49 0 0 21.49 0 48v204.118a48 48 0 0 0 14.059 33.941l211.882 211.882c18.744 18.745 49.136 18.746 67.882 0l204.118-204.118c18.745-18.745 18.745-49.137 0-67.882zM112 160c-26.51 0-48-21.49-48-48s21.49-48 48-48 48 21.49 48 48-21.49 48-48 48zm513.941 133.823L421.823 497.941c-18.745 18.745-49.137 18.745-67.882 0l-.36-.36L527.64 323.522c16.999-16.999 26.36-39.6 26.36-63.64s-9.362-46.641-26.36-63.64L331.397 0h48.721a48 48 0 0 1 33.941 14.059l211.882 211.882c18.745 18.745 18.745 49.137 0 67.882z"></path></svg>标签</a></li><li class="menu-item menu-item-categories"><a href="/categories/"><svg aria-hidden="true" focusable="false" data-prefix="fa" data-icon="th" class="svg-inline--fa fa-th fa-w-16 icon" role="img" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512"><path fill="currentColor" d="M149.333 56v80c0 13.255-10.745 24-24 24H24c-13.255 0-24-10.745-24-24V56c0-13.255 10.745-24 24-24h101.333c13.255 0 24 10.745 24 24zm181.334 240v-80c0-13.255-10.745-24-24-24H205.333c-13.255 0-24 10.745-24 24v80c0 13.255 10.745 24 24 24h101.333c13.256 0 24.001-10.745 24.001-24zm32-240v80c0 13.255 10.745 24 24 24H488c13.255 0 24-10.745 24-24V56c0-13.255-10.745-24-24-24H386.667c-13.255 0-24 10.745-24 24zm-32 80V56c0-13.255-10.745-24-24-24H205.333c-13.255 0-24 10.745-24 24v80c0 13.255 10.745 24 24 24h101.333c13.256 0 24.001-10.745 24.001-24zm-205.334 56H24c-13.255 0-24 10.745-24 24v80c0 13.255 10.745 24 24 24h101.333c13.255 0 24-10.745 24-24v-80c0-13.255-10.745-24-24-24zM0 376v80c0 13.255 10.745 24 24 24h101.333c13.255 0 24-10.745 24-24v-80c0-13.255-10.745-24-24-24H24c-13.255 0-24 10.745-24 24zm386.667-56H488c13.255 0 24-10.745 24-24v-80c0-13.255-10.745-24-24-24H386.667c-13.255 0-24 10.745-24 24v80c0 13.255 10.745 24 24 24zm0 160H488c13.255 0 24-10.745 24-24v-80c0-13.255-10.745-24-24-24H386.667c-13.255 0-24 10.745-24 24v80c0 13.255 10.745 24 24 24zM181.333 376v80c0 13.255 10.745 24 24 24h101.333c13.255 0 24-10.745 24-24v-80c0-13.255-10.745-24-24-24H205.333c-13.255 0-24 10.745-24 24z"></path></svg>分类</a></li><li class="menu-item menu-item-archives"><a href="/archives/"><svg aria-hidden="true" focusable="false" data-prefix="fa" data-icon="archive" class="svg-inline--fa fa-archive fa-w-16 icon" role="img" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512"><path fill="currentColor" d="M32 448c0 17.7 14.3 32 32 32h384c17.7 0 32-14.3 32-32V160H32v288zm160-212c0-6.6 5.4-12 12-12h104c6.6 0 12 5.4 12 12v8c0 6.6-5.4 12-12 12H204c-6.6 0-12-5.4-12-12v-8zM480 32H32C14.3 32 0 46.3 0 64v48c0 8.8 7.2 16 16 16h480c8.8 0 16-7.2 16-16V64c0-17.7-14.3-32-32-32z"></path></svg>归档</a></li><li class="menu-item menu-item-more"><a href="/more/"><svg aria-hidden="true" focusable="false" data-prefix="fa" data-icon="info-circle" class="svg-inline--fa fa-info-circle fa-w-16 icon" role="img" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512"><path fill="currentColor" d="M256 8C119.043 8 8 119.083 8 256c0 136.997 111.043 248 248 248s248-111.003 248-248C504 119.083 392.957 8 256 8zm0 110c23.196 0 42 18.804 42 42s-18.804 42-42 42-42-18.804-42-42 18.804-42 42-42zm56 254c0 6.627-5.373 12-12 12h-88c-6.627 0-12-5.373-12-12v-24c0-6.627 5.373-12 12-12h12v-64h-12c-6.627 0-12-5.373-12-12v-24c0-6.627 5.373-12 12-12h64c6.627 0 12 5.373 12 12v100h12c6.627 0 12 5.373 12 12v24z"></path></svg>More</a></li><li class="menu-item menu-item-search"><a href="javascript:;" class="popup-trigger"><svg aria-hidden="true" focusable="false" data-prefix="fas" data-icon="search" class="svg-inline--fa fa-search fa-w-16 icon" role="img" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512"><path fill="currentColor" d="M505 442.7L405.3 343c-4.5-4.5-10.6-7-17-7H372c27.6-35.3 44-79.7 44-128C416 93.1 322.9 0 208 0S0 93.1 0 208s93.1 208 208 208c48.3 0 92.7-16.4 128-44v16.3c0 6.4 2.5 12.5 7 17l99.7 99.7c9.4 9.4 24.6 9.4 33.9 0l28.3-28.3c9.4-9.4 9.4-24.6.1-34zM208 336c-70.7 0-128-57.2-128-128 0-70.7 57.2-128 128-128 70.7 0 128 57.2 128 128 0 70.7-57.2 128-128 128z"></path></svg>搜索</a></li>  </ul>
</nav>
  </div>

  <div class="sidebar-inner card">

    

    <ul class="sidebar-nav">
      <li class="sidebar-nav-toc sidebar-nav-active">
        文章目录
      </li>
      <li class="sidebar-nav-overview">
        站点概览
      </li>
    </ul>
    <div class="post-toc-wrap sidebar-panel sidebar-panel-active">
      <div class="post-toc"><ol class="nav"><li class="nav-item nav-level-1"><a class="nav-link" href="#%E7%8E%AF%E5%A2%83%E6%90%AD%E5%BB%BA"><span class="nav-number">1.</span> <span class="nav-text">环境搭建</span></a></li><li class="nav-item nav-level-1"><a class="nav-link" href="#Route-Predicate-Factory"><span class="nav-number">2.</span> <span class="nav-text">Route Predicate Factory</span></a></li><li class="nav-item nav-level-1"><a class="nav-link" href="#GatewayFilter-Factory"><span class="nav-number">3.</span> <span class="nav-text">GatewayFilter Factory</span></a></li><li class="nav-item nav-level-1"><a class="nav-link" href="#Others"><span class="nav-number">4.</span> <span class="nav-text">Others</span></a><ol class="nav-child"><li class="nav-item nav-level-2"><a class="nav-link" href="#Fluent-Java-Routes-API"><span class="nav-number">4.1.</span> <span class="nav-text">Fluent Java Routes API</span></a></li><li class="nav-item nav-level-2"><a class="nav-link" href="#Request-Response-Body"><span class="nav-number">4.2.</span> <span class="nav-text">Request&#x2F;Response Body</span></a></li></ol></li><li class="nav-item nav-level-1"><a class="nav-link" href="#%E5%8F%82%E8%80%83"><span class="nav-number">5.</span> <span class="nav-text">参考</span></a></li></ol></div>
    </div>
    <div class="site-overview-wrap sidebar-panel">
      <div class="site-overview">
  <div class="site-author">
    
    <img class="site-author-image" src="/images/avatar.jpg"
      alt="Mr.J" />
    
    <p class="site-author-name">Mr.J</p>
    <div class="site-description">何须更问浮生事，只此浮生是梦中</div>
  </div>

  <nav class="site-state">
    
    <div class="site-state-item site-state-posts">
      <a href="/archives/">
        <span class="site-state-item-count">64</span>
        <span class="site-state-item-name">日志</span>
      </a>
    </div>
    

    
    <div class="site-state-item site-state-categories">
      <a href="/categories/">
        <span class="site-state-item-count">8</span>
        <span class="site-state-item-name">分类</span>
      </a>
    </div>
    

    
    <div class="site-state-item site-state-tags">
      <a href="/tags/">
        <span class="site-state-item-count">48</span>
        <span class="site-state-item-name">标签</span>
      </a>
    </div>
    
  </nav>

  <div class="social-links"><a href="mailto:i@dnocm.com" title="mailto:i@dnocm.com" rel="noopener" target="_blank"><svg aria-hidden="true" focusable="false" data-prefix="fa" data-icon="envelope" class="svg-inline--fa fa-envelope fa-w-16 icon" role="img" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512"><path fill="currentColor" d="M502.3 190.8c3.9-3.1 9.7-.2 9.7 4.7V400c0 26.5-21.5 48-48 48H48c-26.5 0-48-21.5-48-48V195.6c0-5 5.7-7.8 9.7-4.7 22.4 17.4 52.1 39.5 154.1 113.6 21.1 15.4 56.7 47.8 92.2 47.6 35.7.3 72-32.8 92.3-47.6 102-74.1 131.6-96.3 154-113.7zM256 320c23.2.4 56.6-29.2 73.4-41.4 132.7-96.3 142.8-104.7 173.4-128.7 5.8-4.5 9.2-11.5 9.2-18.9v-19c0-26.5-21.5-48-48-48H48C21.5 64 0 85.5 0 112v19c0 7.4 3.4 14.3 9.2 18.9 30.6 23.9 40.7 32.4 173.4 128.7 16.8 12.2 50.2 41.8 73.4 41.4z"></path></svg></a><a href="https://t.me/dnocm" title="https://t.me/dnocm" rel="noopener" target="_blank"><svg aria-hidden="true" focusable="false" data-prefix="fab" data-icon="telegram" class="svg-inline--fa fa-telegram fa-w-16 icon" role="img" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 496 512"><path fill="currentColor" d="M248 8C111 8 0 119 0 256s111 248 248 248 248-111 248-248S385 8 248 8zm121.8 169.9l-40.7 191.8c-3 13.6-11.1 16.9-22.4 10.5l-62-45.7-29.9 28.8c-3.3 3.3-6.1 6.1-12.5 6.1l4.4-63.1 114.9-103.8c5-4.4-1.1-6.9-7.7-2.5l-142 89.4-61.2-19.1c-13.3-4.2-13.6-13.3 2.8-19.7l239.1-92.2c11.1-4 20.8 2.7 17.2 19.5z"></path></svg></a><a href="/rss2.xml" title="/rss2.xml" rel="noopener" target="_blank"><svg aria-hidden="true" focusable="false" data-prefix="fa" data-icon="rss" class="svg-inline--fa fa-rss fa-w-14 icon" role="img" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 448 512"><path fill="currentColor" d="M128.081 415.959c0 35.369-28.672 64.041-64.041 64.041S0 451.328 0 415.959s28.672-64.041 64.041-64.041 64.04 28.673 64.04 64.041zm175.66 47.25c-8.354-154.6-132.185-278.587-286.95-286.95C7.656 175.765 0 183.105 0 192.253v48.069c0 8.415 6.49 15.472 14.887 16.018 111.832 7.284 201.473 96.702 208.772 208.772.547 8.397 7.604 14.887 16.018 14.887h48.069c9.149.001 16.489-7.655 15.995-16.79zm144.249.288C439.596 229.677 251.465 40.445 16.503 32.01 7.473 31.686 0 38.981 0 48.016v48.068c0 8.625 6.835 15.645 15.453 15.999 191.179 7.839 344.627 161.316 352.465 352.465.353 8.618 7.373 15.453 15.999 15.453h48.068c9.034-.001 16.329-7.474 16.005-16.504z"></path></svg></a><a href="https://github.com/JiangTJ" title="https://github.com/JiangTJ" rel="noopener" target="_blank"><svg aria-hidden="true" focusable="false" data-prefix="fab" data-icon="github" class="svg-inline--fa fa-github fa-w-16 icon" role="img" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 496 512"><path fill="currentColor" d="M165.9 397.4c0 2-2.3 3.6-5.2 3.6-3.3.3-5.6-1.3-5.6-3.6 0-2 2.3-3.6 5.2-3.6 3-.3 5.6 1.3 5.6 3.6zm-31.1-4.5c-.7 2 1.3 4.3 4.3 4.9 2.6 1 5.6 0 6.2-2s-1.3-4.3-4.3-5.2c-2.6-.7-5.5.3-6.2 2.3zm44.2-1.7c-2.9.7-4.9 2.6-4.6 4.9.3 2 2.9 3.3 5.9 2.6 2.9-.7 4.9-2.6 4.6-4.6-.3-1.9-3-3.2-5.9-2.9zM244.8 8C106.1 8 0 113.3 0 252c0 110.9 69.8 205.8 169.5 239.2 12.8 2.3 17.3-5.6 17.3-12.1 0-6.2-.3-40.4-.3-61.4 0 0-70 15-84.7-29.8 0 0-11.4-29.1-27.8-36.6 0 0-22.9-15.7 1.6-15.4 0 0 24.9 2 38.6 25.8 21.9 38.6 58.6 27.5 72.9 20.9 2.3-16 8.8-27.1 16-33.7-55.9-6.2-112.3-14.3-112.3-110.5 0-27.5 7.6-41.3 23.6-58.9-2.6-6.5-11.1-33.3 2.6-67.9 20.9-6.5 69 27 69 27 20-5.6 41.5-8.5 62.8-8.5s42.8 2.9 62.8 8.5c0 0 48.1-33.6 69-27 13.7 34.7 5.2 61.4 2.6 67.9 16 17.7 25.8 31.5 25.8 58.9 0 96.5-58.9 104.2-114.8 110.5 9.2 7.9 17 22.9 17 46.4 0 33.7-.3 75.4-.3 83.6 0 6.5 4.6 14.4 17.3 12.1C428.2 457.8 496 362.9 496 252 496 113.3 383.5 8 244.8 8zM97.2 352.9c-1.3 1-1 3.3.7 5.2 1.6 1.6 3.9 2.3 5.2 1 1.3-1 1-3.3-.7-5.2-1.6-1.6-3.9-2.3-5.2-1zm-10.8-8.1c-.7 1.3.3 2.9 2.3 3.9 1.6 1 3.6.7 4.3-.7.7-1.3-.3-2.9-2.3-3.9-2-.6-3.6-.3-4.3.7zm32.4 35.6c-1.6 1.3-1 4.3 1.3 6.2 2.3 2.3 5.2 2.6 6.5 1 1.3-1.3.7-4.3-1.3-6.2-2.2-2.3-5.2-2.6-6.5-1zm-11.4-14.7c-1.6 1-1.6 3.6 0 5.9 1.6 2.3 4.3 3.3 5.6 2.3 1.6-1.3 1.6-3.9 0-6.2-1.4-2.3-4-3.3-5.6-2z"></path></svg></a></div><div class="font-size-ol"><a class="font-size-ol-sub">A-</a><span class="font-size-ol-value">14</span><a class="font-size-ol-plus">A+</a></div>
</div>
    </div>

    

  </div>
</aside>

    <div class="flex-main">

      <main>
        
        <div id="content" class="content">
          <div>
  

<article class="post post-type-normal">
  <div class="card post-block">

    

    <header class="post-header">

      
      <h1 class="post-title">
        
        Spring Cloud 之 Gateway （Greenwich版）
        
      </h1>
      

      <div class="post-meta">

  
  
  <span class="post-meta-item">
    <span class="post-meta-item-icon">
      <svg aria-hidden="true" focusable="false" data-prefix="far" data-icon="calendar" class="svg-inline--fa fa-calendar fa-w-14 icon" role="img" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 448 512"><path fill="currentColor" d="M400 64h-48V12c0-6.6-5.4-12-12-12h-40c-6.6 0-12 5.4-12 12v52H160V12c0-6.6-5.4-12-12-12h-40c-6.6 0-12 5.4-12 12v52H48C21.5 64 0 85.5 0 112v352c0 26.5 21.5 48 48 48h352c26.5 0 48-21.5 48-48V112c0-26.5-21.5-48-48-48zm-6 400H54c-3.3 0-6-2.7-6-6V160h352v298c0 3.3-2.7 6-6 6z"></path></svg>
    </span>
    <span class="post-meta-item-text">发表于</span>
    <time title="创建时间：2019-05-20 21:16:33">2019-05-20</time>
  </span>
  

  

  
  
  <span class="post-meta-item">
    <span class="post-meta-item-icon">
      <svg aria-hidden="true" focusable="false" data-prefix="fa" data-icon="folder" class="svg-inline--fa fa-folder fa-w-16 icon" role="img" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512"><path fill="currentColor" d="M464 128H272l-64-64H48C21.49 64 0 85.49 0 112v288c0 26.51 21.49 48 48 48h416c26.51 0 48-21.49 48-48V176c0-26.51-21.49-48-48-48z"></path></svg>
    </span>
    <span class="post-meta-item-text">分类于</span>
    <span><a href="/categories/%E5%90%8E%E7%AB%AF/" rel="index"><span>后端</span></a></span>  </span>
  

  
  

</div>

    </header>

    <div class="post-body">
      
      <p>众所周知，netflix OSS 2.0 难产了，上一代的zuul网关虽说不错，但其并不是异步的。所以，Spring团队推出了基于Spring Webflux的全新异步的网关–Spring Cloud Gateway。</p>
<blockquote>
<p>本文内容基于Spring Cloud Gateway 2.1.0.GA</p>
</blockquote>
<p>来跟着我一步步，探索它的<del>魅力</del>坑吧！</p>
<span id="more"></span>

<h1 id="环境搭建" class="article-heading"><a href="#环境搭建" class="headerlink" title="环境搭建"></a>环境搭建<a class="article-anchor" href="#环境搭建" aria-hidden="true"></a></h1><p>与所有的微服务组件一样，demo总是很简单的，如果你要启用，创建时勾上相关依赖即可。</p>
<p>就像这样：</p>
<figure class="highlight xml"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br></pre></td><td class="code"><pre><code class="hljs xml"><span class="hljs-tag">&lt;<span class="hljs-name">dependencies</span>&gt;</span><br>        <span class="hljs-tag">&lt;<span class="hljs-name">dependency</span>&gt;</span><br>            <span class="hljs-tag">&lt;<span class="hljs-name">groupId</span>&gt;</span>org.springframework.cloud<span class="hljs-tag">&lt;/<span class="hljs-name">groupId</span>&gt;</span><br>            <span class="hljs-tag">&lt;<span class="hljs-name">artifactId</span>&gt;</span>spring-cloud-starter-gateway<span class="hljs-tag">&lt;/<span class="hljs-name">artifactId</span>&gt;</span><br>        <span class="hljs-tag">&lt;/<span class="hljs-name">dependency</span>&gt;</span><br>        <span class="hljs-tag">&lt;<span class="hljs-name">dependency</span>&gt;</span><br>            <span class="hljs-tag">&lt;<span class="hljs-name">groupId</span>&gt;</span>org.springframework.cloud<span class="hljs-tag">&lt;/<span class="hljs-name">groupId</span>&gt;</span><br>            <span class="hljs-tag">&lt;<span class="hljs-name">artifactId</span>&gt;</span>spring-cloud-starter-netflix-eureka-client<span class="hljs-tag">&lt;/<span class="hljs-name">artifactId</span>&gt;</span><br>        <span class="hljs-tag">&lt;/<span class="hljs-name">dependency</span>&gt;</span><br>        <span class="hljs-tag">&lt;<span class="hljs-name">dependency</span>&gt;</span><br>            <span class="hljs-tag">&lt;<span class="hljs-name">groupId</span>&gt;</span>org.springframework.cloud<span class="hljs-tag">&lt;/<span class="hljs-name">groupId</span>&gt;</span><br>            <span class="hljs-tag">&lt;<span class="hljs-name">artifactId</span>&gt;</span>spring-cloud-starter-netflix-hystrix<span class="hljs-tag">&lt;/<span class="hljs-name">artifactId</span>&gt;</span><br>        <span class="hljs-tag">&lt;/<span class="hljs-name">dependency</span>&gt;</span><br><br>        <span class="hljs-tag">&lt;<span class="hljs-name">dependency</span>&gt;</span><br>            <span class="hljs-tag">&lt;<span class="hljs-name">groupId</span>&gt;</span>org.springframework.boot<span class="hljs-tag">&lt;/<span class="hljs-name">groupId</span>&gt;</span><br>            <span class="hljs-tag">&lt;<span class="hljs-name">artifactId</span>&gt;</span>spring-boot-configuration-processor<span class="hljs-tag">&lt;/<span class="hljs-name">artifactId</span>&gt;</span><br>            <span class="hljs-tag">&lt;<span class="hljs-name">optional</span>&gt;</span>true<span class="hljs-tag">&lt;/<span class="hljs-name">optional</span>&gt;</span><br>        <span class="hljs-tag">&lt;/<span class="hljs-name">dependency</span>&gt;</span><br>        <span class="hljs-tag">&lt;<span class="hljs-name">dependency</span>&gt;</span><br>            <span class="hljs-tag">&lt;<span class="hljs-name">groupId</span>&gt;</span>org.projectlombok<span class="hljs-tag">&lt;/<span class="hljs-name">groupId</span>&gt;</span><br>            <span class="hljs-tag">&lt;<span class="hljs-name">artifactId</span>&gt;</span>lombok<span class="hljs-tag">&lt;/<span class="hljs-name">artifactId</span>&gt;</span><br>            <span class="hljs-tag">&lt;<span class="hljs-name">optional</span>&gt;</span>true<span class="hljs-tag">&lt;/<span class="hljs-name">optional</span>&gt;</span><br>        <span class="hljs-tag">&lt;/<span class="hljs-name">dependency</span>&gt;</span><br>        <span class="hljs-tag">&lt;<span class="hljs-name">dependency</span>&gt;</span><br>            <span class="hljs-tag">&lt;<span class="hljs-name">groupId</span>&gt;</span>org.springframework.boot<span class="hljs-tag">&lt;/<span class="hljs-name">groupId</span>&gt;</span><br>            <span class="hljs-tag">&lt;<span class="hljs-name">artifactId</span>&gt;</span>spring-boot-starter-test<span class="hljs-tag">&lt;/<span class="hljs-name">artifactId</span>&gt;</span><br>            <span class="hljs-tag">&lt;<span class="hljs-name">scope</span>&gt;</span>test<span class="hljs-tag">&lt;/<span class="hljs-name">scope</span>&gt;</span><br>        <span class="hljs-tag">&lt;/<span class="hljs-name">dependency</span>&gt;</span><br>    <span class="hljs-tag">&lt;/<span class="hljs-name">dependencies</span>&gt;</span><br></code></pre></td></tr></table></figure>
<p>其中<code>lombok</code>是我的习惯，你可以选择不添加。</p>
<p>启动类修改为<code>@SpringCloudApplication</code>。</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><code class="hljs java"><span class="hljs-meta">@SpringCloudApplication</span><br><span class="hljs-keyword">public</span> <span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">SpringCloudGatewayApplication</span> </span>&#123;<br>    <span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">static</span> <span class="hljs-keyword">void</span> <span class="hljs-title">main</span><span class="hljs-params">(String[] args)</span> </span>&#123;<br>        SpringApplication.run(SpringCloudGatewayApplication.class, args);<br>    &#125;<br>&#125;<br></code></pre></td></tr></table></figure>

<p>添加些简单配置（一个路由），跳转到我的博客，直接填写了url。由于只涉及网关，所以我把不必要的eureka关了，但实际开发中需要使用它，并添加ribbon路由<code>lb://&lt;service-name&gt;</code></p>
<figure class="highlight yml"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br></pre></td><td class="code"><pre><code class="hljs yml"><span class="hljs-attr">spring:</span><br>  <span class="hljs-attr">cloud:</span><br>    <span class="hljs-attr">gateway:</span><br>      <span class="hljs-attr">routes:</span><br>        <span class="hljs-bullet">-</span> <span class="hljs-attr">id:</span> <span class="hljs-string">app</span><br>          <span class="hljs-attr">uri:</span> <span class="hljs-string">http://www.dnocm.com</span><br>          <span class="hljs-comment"># lb: Ribbon</span><br>          <span class="hljs-comment"># uri: lb://app-name</span><br>          <span class="hljs-attr">predicates:</span><br>            <span class="hljs-bullet">-</span> <span class="hljs-string">Path=/app/**</span><br>          <span class="hljs-attr">filters:</span><br>            <span class="hljs-comment"># Strip first path，such base</span><br>            <span class="hljs-bullet">-</span> <span class="hljs-string">StripPrefix=1</span><br>            <span class="hljs-comment"># - RewritePath=/base/,/</span><br><span class="hljs-attr">eureka:</span><br>  <span class="hljs-attr">client:</span><br>    <span class="hljs-attr">enabled:</span> <span class="hljs-literal">false</span><br></code></pre></td></tr></table></figure>

<p>就此完成，启动运行。当你访问<code>localhost:8080/app/**</code>路由时，都会调整至<code>www.dnocm.com/**</code>。这是因为我设置了<code>http</code>一律<code>302</code>跳转至<code>https</code>。所以，这证明我们的网关搭建完成啦！！</p>
<h1 id="Route-Predicate-Factory" class="article-heading"><a href="#Route-Predicate-Factory" class="headerlink" title="Route Predicate Factory"></a>Route Predicate Factory<a class="article-anchor" href="#Route-Predicate-Factory" aria-hidden="true"></a></h1><p>id uri顾名思义，不多说，但predicates是什么呢？predicates做动词有<code>使基于; 使以…为依据; 表明; 阐明; 断言;</code>的意思，简单说，用于表明在那种条件下，该路由配置生效。</p>
<p>官方提供给我了许多的predicates</p>
<figure class="highlight yml"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br></pre></td><td class="code"><pre><code class="hljs yml"><span class="hljs-attr">spring:</span><br>  <span class="hljs-attr">cloud:</span><br>    <span class="hljs-attr">gateway:</span><br>      <span class="hljs-attr">routes:</span><br>      <span class="hljs-bullet">-</span> <span class="hljs-attr">id:</span> <span class="hljs-string">example</span><br>        <span class="hljs-attr">uri:</span> <span class="hljs-string">http://example.org</span><br>        <span class="hljs-attr">predicates:</span><br>        <span class="hljs-comment"># 匹配在什么时间之后的</span><br>        <span class="hljs-bullet">-</span> <span class="hljs-string">After=2017-01-20T17:42:47.789-07:00[America/Denver]</span><br>        <span class="hljs-comment"># 匹配在什么时间之前的</span><br>        <span class="hljs-bullet">-</span> <span class="hljs-string">Before=2017-01-20T17:42:47.789-07:00[America/Denver]</span><br>        <span class="hljs-comment"># 匹配在某段时间的</span><br>        <span class="hljs-bullet">-</span> <span class="hljs-string">Between=2017-01-20T17:42:47.789-07:00[America/Denver],</span> <span class="hljs-number">2017-01-21T17:42:47.789-07:00</span>[<span class="hljs-string">America/Denver</span>]<br>        <span class="hljs-comment"># 匹配cookie名称为`chocolate`的值要符合`ch.p`正则.</span><br>        <span class="hljs-bullet">-</span> <span class="hljs-string">Cookie=chocolate,</span> <span class="hljs-string">ch.p</span><br>        <span class="hljs-comment"># 匹配header为`X-Request-Id`的值要符合`\d+`正则.</span><br>        <span class="hljs-bullet">-</span> <span class="hljs-string">Header=X-Request-Id,</span> <span class="hljs-string">\d+</span><br>        <span class="hljs-comment"># 匹配任意符合`**.somehost.org`与`**.anotherhost.org`正则的网址</span><br>        <span class="hljs-bullet">-</span> <span class="hljs-string">Host=**.somehost.org,**.anotherhost.org</span><br>        <span class="hljs-comment"># Host还支持模版变量，会保存在`ServerWebExchange.getAttributes()`的 ServerWebExchangeUtils.URI_TEMPLATE_VARIABLES_ATTRIBUTE中，以Map形式存储</span><br>        <span class="hljs-bullet">-</span> <span class="hljs-string">Host=&#123;sub&#125;.myhost.org</span><br>        <span class="hljs-comment"># 匹配GET方法</span><br>        <span class="hljs-bullet">-</span> <span class="hljs-string">Method=GET</span><br>        <span class="hljs-comment"># 路径匹配，与Host一样支持模版变量，存在URI_TEMPLATE_VARIABLES_ATTRIBUTE中。</span><br>        <span class="hljs-bullet">-</span> <span class="hljs-string">Path=/foo/&#123;segment&#125;,/bar/&#123;segment&#125;</span><br>        <span class="hljs-comment"># 匹配存在baz查询参数</span><br>        <span class="hljs-bullet">-</span> <span class="hljs-string">Query=baz</span><br>        <span class="hljs-comment"># 匹配存在foo且符合`ba.`正则</span><br>        <span class="hljs-bullet">-</span> <span class="hljs-string">Query=foo,</span> <span class="hljs-string">ba.</span><br>        <span class="hljs-comment"># 匹配远程地址</span><br>        <span class="hljs-bullet">-</span> <span class="hljs-string">RemoteAddr=192.168.1.1/24</span><br></code></pre></td></tr></table></figure>

<p>官方几乎提供了我们所需的全部功能，这点值得鼓掌👏，然而假如遇到无法满足的情况呢？我们翻阅文档，发现自定义部分是大写的<code>TBD</code>待定ヾ(｡｀Д´｡)。</p>
<p>那么怎么办呢？我们从官方的Predicate Factory看起，去学习。</p>
<p>挑个简单的<code>HeaderRoutePredicateFactory</code></p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br></pre></td><td class="code"><pre><code class="hljs java"><span class="hljs-keyword">public</span> <span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">HeaderRoutePredicateFactory</span> <span class="hljs-keyword">extends</span> <span class="hljs-title">AbstractRoutePredicateFactory</span>&lt;<span class="hljs-title">HeaderRoutePredicateFactory</span>.<span class="hljs-title">Config</span>&gt; </span>&#123;<br><br>  <span class="hljs-keyword">public</span> <span class="hljs-keyword">static</span> <span class="hljs-keyword">final</span> String HEADER_KEY = <span class="hljs-string">&quot;header&quot;</span>;<br>  <span class="hljs-keyword">public</span> <span class="hljs-keyword">static</span> <span class="hljs-keyword">final</span> String REGEXP_KEY = <span class="hljs-string">&quot;regexp&quot;</span>;<br><br>  <span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-title">HeaderRoutePredicateFactory</span><span class="hljs-params">()</span> </span>&#123;<br>    <span class="hljs-keyword">super</span>(Config.class);<br>  &#125;<br><br>  <span class="hljs-meta">@Override</span><br>  <span class="hljs-function"><span class="hljs-keyword">public</span> List&lt;String&gt; <span class="hljs-title">shortcutFieldOrder</span><span class="hljs-params">()</span> </span>&#123;<br>    <span class="hljs-keyword">return</span> Arrays.asList(HEADER_KEY, REGEXP_KEY);<br>  &#125;<br><br>  <span class="hljs-meta">@Override</span><br>  <span class="hljs-function"><span class="hljs-keyword">public</span> Predicate&lt;ServerWebExchange&gt; <span class="hljs-title">apply</span><span class="hljs-params">(Config config)</span> </span>&#123;<br>    <span class="hljs-keyword">boolean</span> hasRegex = !StringUtils.isEmpty(config.regexp);<br><br>    <span class="hljs-keyword">return</span> exchange -&gt; &#123;<br>      List&lt;String&gt; values = exchange.getRequest().getHeaders()<br>          .getOrDefault(config.header, Collections.emptyList());<br>      <span class="hljs-keyword">if</span> (values.isEmpty()) &#123;<br>        <span class="hljs-keyword">return</span> <span class="hljs-keyword">false</span>;<br>      &#125;<br>      <span class="hljs-comment">// values is now guaranteed to not be empty</span><br>      <span class="hljs-keyword">if</span> (hasRegex) &#123;<br>        <span class="hljs-comment">// check if a header value matches</span><br>        <span class="hljs-keyword">return</span> values.stream().anyMatch(value -&gt; value.matches(config.regexp));<br>      &#125;<br><br>      <span class="hljs-comment">// there is a value and since regexp is empty, we only check existence.</span><br>      <span class="hljs-keyword">return</span> <span class="hljs-keyword">true</span>;<br>    &#125;;<br>  &#125;<br><br>  <span class="hljs-meta">@Validated</span><br>  <span class="hljs-keyword">public</span> <span class="hljs-keyword">static</span> <span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">Config</span> </span>&#123;<br><br>    <span class="hljs-meta">@NotEmpty</span><br>    <span class="hljs-keyword">private</span> String header;<br><br>    <span class="hljs-keyword">private</span> String regexp;<br><br>    <span class="hljs-function"><span class="hljs-keyword">public</span> String <span class="hljs-title">getHeader</span><span class="hljs-params">()</span> </span>&#123;<br>      <span class="hljs-keyword">return</span> header;<br>    &#125;<br><br>    <span class="hljs-function"><span class="hljs-keyword">public</span> Config <span class="hljs-title">setHeader</span><span class="hljs-params">(String header)</span> </span>&#123;<br>      <span class="hljs-keyword">this</span>.header = header;<br>      <span class="hljs-keyword">return</span> <span class="hljs-keyword">this</span>;<br>    &#125;<br><br>    <span class="hljs-function"><span class="hljs-keyword">public</span> String <span class="hljs-title">getRegexp</span><span class="hljs-params">()</span> </span>&#123;<br>      <span class="hljs-keyword">return</span> regexp;<br>    &#125;<br><br>    <span class="hljs-function"><span class="hljs-keyword">public</span> Config <span class="hljs-title">setRegexp</span><span class="hljs-params">(String regexp)</span> </span>&#123;<br>      <span class="hljs-keyword">this</span>.regexp = regexp;<br>      <span class="hljs-keyword">return</span> <span class="hljs-keyword">this</span>;<br>    &#125;<br><br>  &#125;<br><br>&#125;<br></code></pre></td></tr></table></figure>
<p>上面的例子，我们可以看出</p>
<ul>
<li>HeaderRoutePredicateFactory的构造方式与继承类视乎是固定的，目的是传递配置类</li>
<li>需要实现<code>Predicate&lt;ServerWebExchange&gt; apply(Consumer&lt;C&gt; consumer)</code></li>
<li>shortcutFieldOrder()似乎是为了配置值与配置类属性对应的</li>
<li>需要定义接受的配置类</li>
<li>查看继承可以发现，它通过<code>NameUtils.normalizeRoutePredicateName(this.getClass())</code>来获取配置文件中的名称</li>
</ul>
<p>Okay，验证上面的内容，我们重新编写一个<code>NonHeaderRoutePredicateFactory</code>，与head取反。同时配置类属性的交换位置。</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br></pre></td><td class="code"><pre><code class="hljs java"><span class="hljs-keyword">public</span> <span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">NonHeaderRoutePredicateFactory</span> <span class="hljs-keyword">extends</span> <span class="hljs-title">AbstractRoutePredicateFactory</span>&lt;<span class="hljs-title">NonHeaderRoutePredicateFactory</span>.<span class="hljs-title">Config</span>&gt; </span>&#123;<br><br>  <span class="hljs-keyword">public</span> <span class="hljs-keyword">static</span> <span class="hljs-keyword">final</span> String HEADER_KEY = <span class="hljs-string">&quot;header&quot;</span>;<br>  <span class="hljs-keyword">public</span> <span class="hljs-keyword">static</span> <span class="hljs-keyword">final</span> String REGEXP_KEY = <span class="hljs-string">&quot;regexp&quot;</span>;<br><br>  <span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-title">NonHeaderRoutePredicateFactory</span><span class="hljs-params">()</span> </span>&#123;<br>    <span class="hljs-keyword">super</span>(Config.class);<br>  &#125;<br>  <span class="hljs-meta">@Override</span><br>  <span class="hljs-function"><span class="hljs-keyword">public</span> List&lt;String&gt; <span class="hljs-title">shortcutFieldOrder</span><span class="hljs-params">()</span> </span>&#123;<br>    <span class="hljs-keyword">return</span> Arrays.asList(HEADER_KEY, REGEXP_KEY);<br>  &#125;<br>  <span class="hljs-meta">@Override</span><br>  <span class="hljs-function"><span class="hljs-keyword">public</span> Predicate&lt;ServerWebExchange&gt; <span class="hljs-title">apply</span><span class="hljs-params">(Config config)</span> </span>&#123;<br>    <span class="hljs-keyword">boolean</span> hasRegex = !StringUtils.isEmpty(config.regexp);<br>    <span class="hljs-keyword">return</span> exchange -&gt; &#123;<br>      List&lt;String&gt; values = exchange.getRequest().getHeaders().getOrDefault(config.header, Collections.emptyList());<br>      <span class="hljs-keyword">if</span> (values.isEmpty()) &#123;<br>        <span class="hljs-keyword">return</span> <span class="hljs-keyword">true</span>;<br>      &#125;<br>      <span class="hljs-keyword">if</span> (hasRegex) &#123;<br>        <span class="hljs-keyword">return</span> values.stream().noneMatch(value -&gt; value.matches(config.regexp));<br>      &#125;<br>      <span class="hljs-keyword">return</span> <span class="hljs-keyword">false</span>;<br>    &#125;;<br>  &#125;<br>  <span class="hljs-meta">@Data</span><br>  <span class="hljs-meta">@Validated</span><br>  <span class="hljs-keyword">public</span> <span class="hljs-keyword">static</span> <span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">Config</span> </span>&#123;<br>    <span class="hljs-keyword">private</span> String regexp;<br>    <span class="hljs-meta">@NotEmpty</span><br>    <span class="hljs-keyword">private</span> String header;<br>  &#125;<br>&#125;<br></code></pre></td></tr></table></figure>

<p>配置添加<code>- NonHeader=tt</code>，当存在<code>tt</code>header时 <code>404 ERROR</code>，不存在时，正常访问。符合推测！</p>
<h1 id="GatewayFilter-Factory" class="article-heading"><a href="#GatewayFilter-Factory" class="headerlink" title="GatewayFilter Factory"></a>GatewayFilter Factory<a class="article-anchor" href="#GatewayFilter-Factory" aria-hidden="true"></a></h1><p>除此predicates外，还有filter，用于过滤请求。与predicates一样，Spring官方也提供了需要内置的过滤器。过滤器部分相对于predicates来说难得多，有全局的也有可配置的。甚至一些过滤器不支持通过配置文件来修改。</p>
<figure class="highlight yml"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><span class="line">66</span><br></pre></td><td class="code"><pre><code class="hljs yml"><span class="hljs-attr">spring:</span><br>  <span class="hljs-attr">cloud:</span><br>    <span class="hljs-attr">gateway:</span><br>      <span class="hljs-attr">routes:</span><br>      <span class="hljs-bullet">-</span> <span class="hljs-attr">id:</span> <span class="hljs-string">example</span><br>        <span class="hljs-attr">uri:</span> <span class="hljs-string">http://example.org</span><br>        <span class="hljs-attr">filters:</span><br>          <span class="hljs-comment"># 先介绍简单的路由器</span><br>          <span class="hljs-comment"># 对header的操作（添加，删除，设置），以及保留原始host的header</span><br>          <span class="hljs-bullet">-</span> <span class="hljs-string">AddRequestHeader=X-Request-Foo,</span> <span class="hljs-string">Bar</span><br>          <span class="hljs-bullet">-</span> <span class="hljs-string">AddResponseHeader=X-Response-Foo,</span> <span class="hljs-string">Bar</span><br>          <span class="hljs-bullet">-</span> <span class="hljs-string">RemoveRequestHeader=X-Request-Foo</span><br>          <span class="hljs-bullet">-</span> <span class="hljs-string">RemoveResponseHeader=X-Response-Foo</span><br>          <span class="hljs-bullet">-</span> <span class="hljs-string">RewriteResponseHeader=X-Response-Foo,</span> <span class="hljs-string">,</span> <span class="hljs-string">password=[^&amp;]+,</span> <span class="hljs-string">password=***</span><br>          <span class="hljs-bullet">-</span> <span class="hljs-string">SetResponseHeader=X-Response-Foo,</span> <span class="hljs-string">Bar</span><br>          <span class="hljs-bullet">-</span> <span class="hljs-string">PreserveHostHeader</span><br>          <span class="hljs-comment"># 对查询参数的过滤</span><br>          <span class="hljs-bullet">-</span> <span class="hljs-string">AddRequestParameter=foo,</span> <span class="hljs-string">bar</span><br>          <span class="hljs-comment"># 对Uri的过滤（path与status）</span><br>          <span class="hljs-bullet">-</span> <span class="hljs-string">PrefixPath=/mypath</span><br>          <span class="hljs-bullet">-</span> <span class="hljs-string">RewritePath=/foo/(?&lt;segment&gt;.*),</span> <span class="hljs-string">/$\&#123;segment&#125;</span><br>          <span class="hljs-bullet">-</span> <span class="hljs-string">SetPath=/&#123;segment&#125;</span><br>          <span class="hljs-bullet">-</span> <span class="hljs-string">StripPrefix=2</span><br>          <span class="hljs-bullet">-</span> <span class="hljs-string">SetStatus=BAD_REQUEST</span><br>          <span class="hljs-bullet">-</span> <span class="hljs-string">SetStatus=401</span><br>          <span class="hljs-bullet">-</span> <span class="hljs-string">RedirectTo=302,</span> <span class="hljs-string">http://acme.org</span><br>          <span class="hljs-comment"># 保留session，默认情况下是不保留的</span><br>          <span class="hljs-bullet">-</span> <span class="hljs-string">SaveSession</span><br>          <span class="hljs-comment"># 设置最大请求数据大小，这里发现一种新写法，理论上predicates中也能使用</span><br>          <span class="hljs-bullet">-</span> <span class="hljs-attr">name:</span> <span class="hljs-string">RequestSize</span><br>            <span class="hljs-attr">args:</span><br>              <span class="hljs-attr">maxSize:</span> <span class="hljs-number">5000000</span><br>          <span class="hljs-comment"># 重试次数设置</span><br>          <span class="hljs-bullet">-</span> <span class="hljs-attr">name:</span> <span class="hljs-string">Retry</span><br>            <span class="hljs-attr">args:</span><br>              <span class="hljs-attr">retries:</span> <span class="hljs-number">3</span><br>              <span class="hljs-attr">statuses:</span> <span class="hljs-string">BAD_GATEWAY</span><br>        <br>          <span class="hljs-comment"># 断路器的配置</span><br>          <span class="hljs-comment"># 断路器的配置比较复杂，首先指定断路器命令名即可启用断路器（这块我也不熟，需要HystrixCommand的内容）</span><br>          <span class="hljs-bullet">-</span> <span class="hljs-string">Hystrix=myCommandName</span><br>          <span class="hljs-comment"># 另外我们可以设置些错误发生后的跳转，当然现在仅支持forward:</span><br>          <span class="hljs-bullet">-</span> <span class="hljs-attr">name:</span> <span class="hljs-string">Hystrix</span><br>            <span class="hljs-attr">args:</span><br>              <span class="hljs-attr">name:</span> <span class="hljs-string">fallbackcmd</span><br>              <span class="hljs-attr">fallbackUri:</span> <span class="hljs-string">forward:/incaseoffailureusethis</span><br>          <span class="hljs-comment"># 我们还可以修改错误信息放置的header</span><br>          <span class="hljs-bullet">-</span> <span class="hljs-attr">name:</span> <span class="hljs-string">FallbackHeaders</span><br>            <span class="hljs-attr">args:</span><br>              <span class="hljs-attr">executionExceptionTypeHeaderName:</span> <span class="hljs-string">Test-Header</span><br>              <span class="hljs-attr">executionExceptionMessageHeaderName:</span> <span class="hljs-string">Test-Header</span><br>              <span class="hljs-attr">rootCauseExceptionTypeHeaderName:</span> <span class="hljs-string">Test-Header</span><br>              <span class="hljs-attr">rootCauseExceptionMessageHeaderName:</span> <span class="hljs-string">Test-Header</span><br><br>           <span class="hljs-comment"># 另一块比较困难的是速率限制</span><br>          <span class="hljs-comment"># 它由RateLimiter的实现来完成的，默认只支持redis，需要添加`spring-boot-starter-data-redis-reactive`依赖</span><br>          <span class="hljs-comment"># 我们需要提供KeyResolver的实现，因为默认会使用PrincipalNameKeyResolver，在不使用Spring Security的情况下几乎不会用到Principal</span><br>          <span class="hljs-comment"># 除此外，我们也可以提供自定义的RateLimiter，#&#123;@myRateLimiter&#125;是一个SpEL表达式，用于从Spring上下文内读取bean</span><br>          <span class="hljs-bullet">-</span> <span class="hljs-attr">name:</span> <span class="hljs-string">RequestRateLimiter</span><br>            <span class="hljs-attr">args:</span><br>              <span class="hljs-attr">redis-rate-limiter.replenishRate:</span> <span class="hljs-number">10</span><br>              <span class="hljs-attr">redis-rate-limiter.burstCapacity:</span> <span class="hljs-number">20</span><br>          <span class="hljs-bullet">-</span> <span class="hljs-attr">name:</span> <span class="hljs-string">RequestRateLimiter</span><br>            <span class="hljs-attr">args:</span><br>              <span class="hljs-attr">rate-limiter:</span> <span class="hljs-string">&quot;#&#123;@myRateLimiter&#125;&quot;</span><br>              <span class="hljs-attr">key-resolver:</span> <span class="hljs-string">&quot;#&#123;@userKeyResolver&#125;&quot;</span><br></code></pre></td></tr></table></figure>

<p>KeyResolver的实现参考</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><code class="hljs java"><span class="hljs-meta">@Bean</span><br><span class="hljs-function">KeyResolver <span class="hljs-title">userKeyResolver</span><span class="hljs-params">()</span> </span>&#123;<br>    <span class="hljs-keyword">return</span> exchange -&gt; Mono.just(exchange.getRequest().getQueryParams().getFirst(<span class="hljs-string">&quot;user&quot;</span>));<br>&#125;<br></code></pre></td></tr></table></figure>

<p>除此外还有两个“特别”的过滤器，<code>modifyRequestBody</code> <code>modifyResponseBody</code>他们只能使用在Fluent Java Routes API中。例如：</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></pre></td><td class="code"><pre><code class="hljs java"><span class="hljs-meta">@Bean</span><br><span class="hljs-function"><span class="hljs-keyword">public</span> RouteLocator <span class="hljs-title">routes</span><span class="hljs-params">(RouteLocatorBuilder builder)</span> </span>&#123;<br>    <span class="hljs-keyword">return</span> builder.routes()<br>        .route(<span class="hljs-string">&quot;rewrite_request_obj&quot;</span>, r -&gt; r.host(<span class="hljs-string">&quot;*.rewriterequestobj.org&quot;</span>)<br>            .filters(f -&gt; f.prefixPath(<span class="hljs-string">&quot;/httpbin&quot;</span>)<br>                .modifyRequestBody(String.class, Hello.class, MediaType.APPLICATION_JSON_VALUE,<br>                    (exchange, s) -&gt; <span class="hljs-keyword">return</span> Mono.just(<span class="hljs-keyword">new</span> Hello(s.toUpperCase())))).uri(uri))<br>        .build();<br>&#125;<br></code></pre></td></tr></table></figure>

<p>此外，这两个过滤器目前处在Beta中，不稳定。而且，Spring团队对于Body的处理十分愚蠢，我会在Others章节提及。</p>
<p>对于全局过滤器，目前系统提供的一般都用于支持基础功能。如负载均衡、路由转换、生成Response等等。对于我们来说，需要关心这些全局过滤器的顺序，毕竟他们与上面的过滤器会一同工作。</p>
<p>与predicates类似，filter也提供了自定义的能力，相对于鸡肋的predicate的自定义，filter显得有用的多。也可能因此，它居然有官方文档介绍（在predicate中是TBD）。我们可以使用它来完成权限的鉴定与下发，一个好的方案是，网关与客户端之间通过session保存用户的登录状态，在网关内，微服务间的沟通使用JWT来认证安全信息。那么我们需要由过滤器来完成这些工作，一个例子如下：</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><span class="line">66</span><br><span class="line">67</span><br><span class="line">68</span><br><span class="line">69</span><br><span class="line">70</span><br><span class="line">71</span><br><span class="line">72</span><br><span class="line">73</span><br><span class="line">74</span><br><span class="line">75</span><br><span class="line">76</span><br><span class="line">77</span><br><span class="line">78</span><br><span class="line">79</span><br><span class="line">80</span><br><span class="line">81</span><br></pre></td><td class="code"><pre><code class="hljs java"><span class="hljs-meta">@Configuration</span><br><span class="hljs-keyword">public</span> <span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">GenerateJwtGatewayFilterFactory</span> <span class="hljs-keyword">extends</span> <span class="hljs-title">AbstractGatewayFilterFactory</span>&lt;<span class="hljs-title">GenerateJwtGatewayFilterFactory</span>.<span class="hljs-title">Config</span>&gt; </span>&#123;<br><br>    <span class="hljs-meta">@Resource</span><br>    <span class="hljs-keyword">private</span> JwtProperties properties;<br>    <span class="hljs-meta">@Resource</span><br>    <span class="hljs-keyword">private</span> JwtAuthServer jwtAuthServer;<br><br>    <span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-title">GenerateJwtGatewayFilterFactory</span><span class="hljs-params">()</span> </span>&#123;<br>        <span class="hljs-keyword">super</span>(Config.class);<br>    &#125;<br><br>    <span class="hljs-meta">@Override</span><br>    <span class="hljs-function"><span class="hljs-keyword">public</span> String <span class="hljs-title">name</span><span class="hljs-params">()</span> </span>&#123;<br>        <span class="hljs-keyword">return</span> <span class="hljs-string">&quot;GenerateJwt&quot;</span>;<br>    &#125;<br><br>    <span class="hljs-meta">@Override</span><br>    <span class="hljs-function"><span class="hljs-keyword">public</span> GatewayFilter <span class="hljs-title">apply</span><span class="hljs-params">(Config config)</span> </span>&#123;<br><br>        String[] place = config.getPlace().split(<span class="hljs-string">&quot;:&quot;</span>);<br><br>        <span class="hljs-keyword">return</span> (exchange, chain) -&gt; &#123;<br>            <span class="hljs-keyword">return</span> Mono<br>                    .defer(() -&gt; &#123;<br>                        <span class="hljs-keyword">if</span> (<span class="hljs-string">&quot;session&quot;</span>.equals(place[<span class="hljs-number">0</span>])) &#123;<br>                            <span class="hljs-keyword">return</span> exchange.getSession().map(webSession -&gt; &#123;<br>                                <span class="hljs-keyword">return</span> webSession.getAttributes().getOrDefault(place[<span class="hljs-number">1</span>], <span class="hljs-string">&quot;&quot;</span>);<br>                            &#125;);<br>                        &#125;<br>                        <span class="hljs-keyword">if</span> (<span class="hljs-string">&quot;query&quot;</span>.equals(place[<span class="hljs-number">0</span>])) &#123;<br>                            String first = exchange.getRequest().getQueryParams().getFirst(place[<span class="hljs-number">1</span>]);<br>                            <span class="hljs-keyword">return</span> Mono.justOrEmpty(first);<br>                        &#125;<br>                        <span class="hljs-keyword">if</span> (<span class="hljs-string">&quot;form&quot;</span>.equals(place[<span class="hljs-number">0</span>])) &#123;<br>                            <span class="hljs-comment">/*return exchange.getFormData().map(formData -&gt; &#123;</span><br><span class="hljs-comment">                                String first = formData.getFirst(place[1]);</span><br><span class="hljs-comment">                                return Optional.ofNullable(first).orElse(&quot;&quot;);</span><br><span class="hljs-comment">                            &#125;);*/</span><br>                            <span class="hljs-keyword">return</span> <span class="hljs-keyword">new</span> DefaultServerRequest(exchange).bodyToMono(<span class="hljs-keyword">new</span> ParameterizedTypeReference&lt;MultiValueMap&lt;String, String&gt;&gt;() &#123;&#125;).map(formData -&gt; &#123;<br>                                String first = formData.getFirst(place[<span class="hljs-number">1</span>]);<br>                                <span class="hljs-keyword">return</span> Optional.ofNullable(first).orElse(<span class="hljs-string">&quot;&quot;</span>);<br>                            &#125;);<br>                        &#125;<br>                        <span class="hljs-keyword">throw</span> <span class="hljs-keyword">new</span> BaimiException(<span class="hljs-string">&quot;不支持的类型！&quot;</span>);<br>                    &#125;)<br>                    .filter(sub -&gt; !StringUtils.isEmpty(sub))<br>                    .map(sub -&gt; jwtAuthServer.generate(config.getAudience(), config.getPrefix() + <span class="hljs-string">&quot;:&quot;</span> + sub))<br>                    .map(token -&gt; exchange.getRequest().mutate().header(properties.getHeaderName(), properties.getHeaderPrefix() + token).build())<br>                    .map(req -&gt; exchange.mutate().request(req).build())<br>                    .then(chain.filter(exchange));<br>        &#125;;<br>    &#125;<br><br>    <span class="hljs-meta">@Override</span><br>    <span class="hljs-function"><span class="hljs-keyword">public</span> List&lt;String&gt; <span class="hljs-title">shortcutFieldOrder</span><span class="hljs-params">()</span> </span>&#123;<br>        <span class="hljs-keyword">return</span> Arrays.asList(<span class="hljs-string">&quot;place&quot;</span>, <span class="hljs-string">&quot;audience&quot;</span>, <span class="hljs-string">&quot;prefix&quot;</span>);<br>    &#125;<br><br>    <span class="hljs-meta">@Data</span><br>    <span class="hljs-keyword">static</span> <span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">Config</span> </span>&#123;<br>        <span class="hljs-comment">/**</span><br><span class="hljs-comment">         * 三种类型的位置.</span><br><span class="hljs-comment">         * - session:&lt;param&gt;</span><br><span class="hljs-comment">         * - query:&lt;param&gt;</span><br><span class="hljs-comment">         * - form:&lt;param&gt;</span><br><span class="hljs-comment">         */</span><br>        <span class="hljs-keyword">private</span> String place = <span class="hljs-string">&quot;session:user&quot;</span>;<br>        <span class="hljs-comment">/**</span><br><span class="hljs-comment">         * 授权对象</span><br><span class="hljs-comment">         * system: 系统用户</span><br><span class="hljs-comment">         * wechat: 微信用户</span><br><span class="hljs-comment">         * etc</span><br><span class="hljs-comment">         */</span><br>        <span class="hljs-keyword">private</span> String audience = <span class="hljs-string">&quot;system&quot;</span>;<br>        <span class="hljs-comment">/**</span><br><span class="hljs-comment">         * 授权主体标识</span><br><span class="hljs-comment">         */</span><br>        <span class="hljs-keyword">private</span> String prefix = <span class="hljs-string">&quot;id&quot;</span>;<br>    &#125;<br>&#125;<br></code></pre></td></tr></table></figure>

<p>在配置文件中，直接使用，更多代码见下面参考中的项目源码。</p>
<figure class="highlight yml"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><code class="hljs yml"><span class="hljs-attr">spring:</span><br>  <span class="hljs-attr">cloud:</span><br>    <span class="hljs-attr">gateway:</span><br>      <span class="hljs-attr">routes:</span><br>        <span class="hljs-bullet">-</span> <span class="hljs-attr">id:</span> <span class="hljs-string">example</span><br>          <span class="hljs-attr">uri:</span> <span class="hljs-string">http://example.org</span><br>          <span class="hljs-attr">filters:</span><br>            <span class="hljs-bullet">-</span> <span class="hljs-string">GenerateJwt=form:id,system,id</span><br></code></pre></td></tr></table></figure>

<p>全局的过滤器也能自定义，像下面一样</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br></pre></td><td class="code"><pre><code class="hljs java"><span class="hljs-meta">@Bean</span><br><span class="hljs-meta">@Order(1)</span><br><span class="hljs-function"><span class="hljs-keyword">public</span> GlobalFilter <span class="hljs-title">c</span><span class="hljs-params">()</span> </span>&#123;<br>    <span class="hljs-keyword">return</span> (exchange, chain) -&gt; &#123;<br>        log.info(<span class="hljs-string">&quot;third pre filter&quot;</span>);<br>        <span class="hljs-keyword">return</span> chain.filter(exchange).then(Mono.fromRunnable(() -&gt; &#123;<br>            log.info(<span class="hljs-string">&quot;first post filter&quot;</span>);<br>        &#125;));<br>    &#125;;<br>&#125;<br></code></pre></td></tr></table></figure>

<h1 id="Others" class="article-heading"><a href="#Others" class="headerlink" title="Others"></a>Others<a class="article-anchor" href="#Others" aria-hidden="true"></a></h1><h2 id="Fluent-Java-Routes-API" class="article-heading"><a href="#Fluent-Java-Routes-API" class="headerlink" title="Fluent Java Routes API"></a>Fluent Java Routes API<a class="article-anchor" href="#Fluent-Java-Routes-API" aria-hidden="true"></a></h2><p>关于Java DSL，个人是极度不推荐使用。由于修改后需要重新打包部署。如果由配置文件决定，我们仅需修改配置文件，重新运行即可，程序会更加稳定，因为它仅提供功能给配置文件使用。</p>
<h2 id="Request-Response-Body" class="article-heading"><a href="#Request-Response-Body" class="headerlink" title="Request/Response Body"></a>Request/Response Body<a class="article-anchor" href="#Request-Response-Body" aria-hidden="true"></a></h2><blockquote>
<p>IllegalStateException 问题范围为 Spring Cloud Gateway 2.0.0 至 2.1.1，1.x 理论上正常但未测试，2.1.2已修复。</p>
</blockquote>
<p>关于Body，Spring对于其的操作是，在最初始化阶段，读取Body内容放入Flux流中。之后都是对其操作。详细可以看下<code>AdaptCachedBodyGlobalFilter</code>全局过滤器的源码。</p>
<p>似乎没什么问题是吧，我们就应该在这个操作流内不断的修改Body的内容，直至其被最终消费（转发）。但是当我们在过滤中使用<code>exchange.getRequest().getBody()</code>或者<code>exchange.getFormData()</code>之后，我们期望后续Spring是读取我们所产生的流，然而事实上，它仍然产生调用<code>getBody()</code>获取最初的流。流是线性的，已消费过的不能再次被消费！所以，我们无法方便的使用它达到我们的目的（当然Java DSL内有提供内置的过滤器，但我不推荐Java DSL本身）。</p>
<p>对此，我们有两种方案解决这个问题</p>
<ol>
<li>处理完成后的流放入Request/Response中，以便其后续的消费</li>
<li>修改getBody()的行为，缓存body内容，且每次生成新的流支持后续操作</li>
</ol>
<p>由于Request/Response对应的Builder不支持放入Body，所有，方案一每次都需要重新构建Body解码器，就像<code>modifyRequestBody</code>做的一样。。。在不需要修改Body的内容的前提（大部分都是这样的）下，方案二我们可以写成通用的Factory，在适当的位置添加即可，显得更加可操作。</p>
<p>下面是一个filter，用于支持RequestBody的缓存：</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><span class="line">66</span><br><span class="line">67</span><br></pre></td><td class="code"><pre><code class="hljs java"><span class="hljs-meta">@Configuration</span><br><span class="hljs-keyword">public</span> <span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">CacheRequestGatewayFilterFactory</span> <span class="hljs-keyword">extends</span> <span class="hljs-title">AbstractGatewayFilterFactory</span>&lt;<span class="hljs-title">CacheRequestGatewayFilterFactory</span>.<span class="hljs-title">Config</span>&gt; </span>&#123;<br><br>    <span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-title">CacheRequestGatewayFilterFactory</span><span class="hljs-params">()</span> </span>&#123;<br>        <span class="hljs-keyword">super</span>(Config.class);<br>    &#125;<br><br>    <span class="hljs-meta">@Override</span><br>    <span class="hljs-function"><span class="hljs-keyword">public</span> String <span class="hljs-title">name</span><span class="hljs-params">()</span> </span>&#123;<br>        <span class="hljs-keyword">return</span> <span class="hljs-string">&quot;CacheRequest&quot;</span>;<br>    &#125;<br><br>    <span class="hljs-meta">@Override</span><br>    <span class="hljs-function"><span class="hljs-keyword">public</span> GatewayFilter <span class="hljs-title">apply</span><span class="hljs-params">(Config config)</span> </span>&#123;<br>        CacheRequestGatewayFilter cacheRequestGatewayFilter = <span class="hljs-keyword">new</span> CacheRequestGatewayFilter();<br>        Integer order = config.getOrder();<br>        <span class="hljs-keyword">if</span> (order == <span class="hljs-keyword">null</span>) &#123;<br>            <span class="hljs-keyword">return</span> cacheRequestGatewayFilter;<br>        &#125;<br>        <span class="hljs-keyword">return</span> <span class="hljs-keyword">new</span> OrderedGatewayFilter(cacheRequestGatewayFilter, order);<br>    &#125;<br><br>    <span class="hljs-keyword">public</span> <span class="hljs-keyword">static</span> <span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">CacheRequestGatewayFilter</span> <span class="hljs-keyword">implements</span> <span class="hljs-title">GatewayFilter</span> </span>&#123;<br>        <span class="hljs-meta">@Override</span><br>        <span class="hljs-function"><span class="hljs-keyword">public</span> Mono&lt;Void&gt; <span class="hljs-title">filter</span><span class="hljs-params">(ServerWebExchange exchange, GatewayFilterChain chain)</span> </span>&#123;<br><br>            <span class="hljs-comment">//GET DELETE 不过滤</span><br>            HttpMethod method = exchange.getRequest().getMethod();<br>            <span class="hljs-keyword">if</span> (method == <span class="hljs-keyword">null</span> || method.matches(<span class="hljs-string">&quot;GET&quot;</span>) || method.matches(<span class="hljs-string">&quot;DELETE&quot;</span>)) &#123;<br>                <span class="hljs-keyword">return</span> chain.filter(exchange);<br>            &#125;<br><br>            <span class="hljs-keyword">return</span> DataBufferUtils.join(exchange.getRequest().getBody())<br>                    .map(dataBuffer -&gt; &#123;<br>                        <span class="hljs-keyword">byte</span>[] bytes = <span class="hljs-keyword">new</span> <span class="hljs-keyword">byte</span>[dataBuffer.readableByteCount()];<br>                        dataBuffer.read(bytes);<br>                        DataBufferUtils.release(dataBuffer);<br>                        <span class="hljs-keyword">return</span> bytes;<br>                    &#125;)<br>                    .defaultIfEmpty(<span class="hljs-keyword">new</span> <span class="hljs-keyword">byte</span>[<span class="hljs-number">0</span>])<br>                    .flatMap(bytes -&gt; &#123;<br>                        DataBufferFactory dataBufferFactory = exchange.getResponse().bufferFactory();<br>                        ServerHttpRequestDecorator decorator = <span class="hljs-keyword">new</span> ServerHttpRequestDecorator(exchange.getRequest()) &#123;<br>                            <span class="hljs-meta">@Override</span><br>                            <span class="hljs-function"><span class="hljs-keyword">public</span> Flux&lt;DataBuffer&gt; <span class="hljs-title">getBody</span><span class="hljs-params">()</span> </span>&#123;<br>                                <span class="hljs-keyword">if</span> (bytes.length &gt; <span class="hljs-number">0</span>) &#123;<br>                                    <span class="hljs-keyword">return</span> Flux.just(dataBufferFactory.wrap(bytes));<br>                                &#125;<br>                                <span class="hljs-keyword">return</span> Flux.empty();<br>                            &#125;<br>                        &#125;;<br>                        <span class="hljs-keyword">return</span> chain.filter(exchange.mutate().request(decorator).build());<br>                    &#125;);<br>        &#125;<br>    &#125;<br><br>    <span class="hljs-meta">@Override</span><br>    <span class="hljs-function"><span class="hljs-keyword">public</span> List&lt;String&gt; <span class="hljs-title">shortcutFieldOrder</span><span class="hljs-params">()</span> </span>&#123;<br>        <span class="hljs-keyword">return</span> Collections.singletonList(<span class="hljs-string">&quot;order&quot;</span>);<br>    &#125;<br><br>    <span class="hljs-meta">@Data</span><br>    <span class="hljs-keyword">static</span> <span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">Config</span> </span>&#123;<br>        <span class="hljs-keyword">private</span> Integer order;<br>    &#125;<br><br>&#125;<br></code></pre></td></tr></table></figure>

<p>配置文件添加CacheRequest，用于添加过滤器（如果不加，从form中读取数据是会报错的）</p>
<figure class="highlight yml"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br></pre></td><td class="code"><pre><code class="hljs yml"><span class="hljs-attr">spring:</span><br>  <span class="hljs-attr">cloud:</span><br>    <span class="hljs-attr">gateway:</span><br>      <span class="hljs-attr">routes:</span><br>        <span class="hljs-bullet">-</span> <span class="hljs-attr">id:</span> <span class="hljs-string">jwt</span><br>          <span class="hljs-attr">uri:</span> <span class="hljs-string">lb://app-name</span><br>          <span class="hljs-attr">predicates:</span><br>            <span class="hljs-bullet">-</span> <span class="hljs-string">Path=/jwt/**</span><br>          <span class="hljs-attr">filters:</span><br>            <span class="hljs-bullet">-</span> <span class="hljs-string">CacheRequest</span><br>            <span class="hljs-bullet">-</span> <span class="hljs-string">GenerateJwt=form:id,system,id</span><br></code></pre></td></tr></table></figure>

<p>当然，<code>exchange.getFormData()</code>的问题没有解决，需要对Body操作，请使用<code>exchange.getRequest().getBody()</code></p>
<blockquote>
<p>在下方 issues:946 提了简化操作的建议，然后官方添加了相关Cache方法，然后发现不使用这个方法也不出问题。。。问题原因就是<code>AdaptCachedBodyGlobalFilter</code>对body解码器的封装，默认情况下2.1.2中不做处理，所以好了。。。</p>
</blockquote>
<h1 id="参考" class="article-heading"><a href="#参考" class="headerlink" title="参考"></a>参考<a class="article-anchor" href="#参考" aria-hidden="true"></a></h1><ul>
<li><a target="_blank" rel="noopener" href="https://github.com/jiangtj-lab/spring-cloud-gateway-example">项目源码</a></li>
<li><a target="_blank" rel="noopener" href="https://cloud.spring.io/spring-cloud-static/spring-cloud-gateway/2.1.0.RELEASE/single/spring-cloud-gateway.html#_modify_request_body_gatewayfilter_factory">官方文档</a></li>
<li><a target="_blank" rel="noopener" href="https://github.com/spring-cloud/spring-cloud-gateway/issues/946">issues:946 关于Body流操作需要简化的建议</a></li>
</ul>
      
    </div>

    







<div>
  <ul class="post-copyright">
    <li class="post-copyright-author">
      <strong>本文作者： </strong>
      Mr.J
    </li>
    <li class="post-copyright-link">
      <strong>本文链接：</strong>
      <a href="https://jiangtj.com/articles/beechnut/spring-cloud-gateway/">https://jiangtj.com/articles/beechnut/spring-cloud-gateway/</a>
    <li class="post-copyright-license">
      <strong>版权声明： </strong>
      本博客所有文章除特别声明外，均采用 <a target="_blank" rel="noopener" href="https://creativecommons.org/licenses/by-nc-sa/4.0/"><svg aria-hidden="true" focusable="false" data-prefix="fab" data-icon="creative-commons" class="svg-inline--fa fa-creative-commons fa-w-16 icon" role="img" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 496 512"><path fill="currentColor" d="M245.83 214.87l-33.22 17.28c-9.43-19.58-25.24-19.93-27.46-19.93-22.13 0-33.22 14.61-33.22 43.84 0 23.57 9.21 43.84 33.22 43.84 14.47 0 24.65-7.09 30.57-21.26l30.55 15.5c-6.17 11.51-25.69 38.98-65.1 38.98-22.6 0-73.96-10.32-73.96-77.05 0-58.69 43-77.06 72.63-77.06 30.72-.01 52.7 11.95 65.99 35.86zm143.05 0l-32.78 17.28c-9.5-19.77-25.72-19.93-27.9-19.93-22.14 0-33.22 14.61-33.22 43.84 0 23.55 9.23 43.84 33.22 43.84 14.45 0 24.65-7.09 30.54-21.26l31 15.5c-2.1 3.75-21.39 38.98-65.09 38.98-22.69 0-73.96-9.87-73.96-77.05 0-58.67 42.97-77.06 72.63-77.06 30.71-.01 52.58 11.95 65.56 35.86zM247.56 8.05C104.74 8.05 0 123.11 0 256.05c0 138.49 113.6 248 247.56 248 129.93 0 248.44-100.87 248.44-248 0-137.87-106.62-248-248.44-248zm.87 450.81c-112.54 0-203.7-93.04-203.7-202.81 0-105.42 85.43-203.27 203.72-203.27 112.53 0 202.82 89.46 202.82 203.26-.01 121.69-99.68 202.82-202.84 202.82z"></path></svg>BY-NC-SA</a> 许可协议。转载请注明出处！    </li>
  </ul>
</div>


    
<div class="post-tags">
  
  <a href="/tags/Spring-Cloud/" rel="tag"># Spring Cloud</a>
  
  <a href="/tags/%E5%BE%AE%E6%9C%8D%E5%8A%A1/" rel="tag"># 微服务</a>
  
</div>


    
    <div class="reward-container"><div>Buy me a cup of milkshake 🍨.</div><div class="reward-box"><div class="reward-box-card wechat">
  <img class="reward-image" src="/images/wechatpay.png" />
  
</div>
<div class="reward-box-card alipay">
  <img class="reward-image" src="/images/alipay.png" />
  
  <a class="reward-image-url" href="HTTPS://QR.ALIPAY.COM/FKX06416WJNHOWKMRQQFFE">点击跳转</a>
  
</div>
<div class="reward-box-card paypal">
  <img class="reward-image" src="/images/paypal.png" />
  
  <a class="reward-image-url" target="_blank" rel="noopener" href="https://www.paypal.me/jiangtj">https://www.paypal.me/jiangtj</a>
  
</div>
<div class="reward-box-card kofi">
  <img class="reward-image" src="/images/kofi.png" />
  
  <a class="reward-image-url" target="_blank" rel="noopener" href="https://ko-fi.com/jiangtj">https://ko-fi.com/jiangtj</a>
  
</div>
</div><div class="reward-group"><a class="reward-button wechat"><span><svg aria-hidden="true" focusable="false" data-prefix="fab" data-icon="weixin" class="svg-inline--fa fa-weixin fa-w-18 icon" role="img" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512"><path fill="currentColor" d="M385.2 167.6c6.4 0 12.6.3 18.8 1.1C387.4 90.3 303.3 32 207.7 32 100.5 32 13 104.8 13 197.4c0 53.4 29.3 97.5 77.9 131.6l-19.3 58.6 68-34.1c24.4 4.8 43.8 9.7 68.2 9.7 6.2 0 12.1-.3 18.3-.8-4-12.9-6.2-26.6-6.2-40.8-.1-84.9 72.9-154 165.3-154zm-104.5-52.9c14.5 0 24.2 9.7 24.2 24.4 0 14.5-9.7 24.2-24.2 24.2-14.8 0-29.3-9.7-29.3-24.2.1-14.7 14.6-24.4 29.3-24.4zm-136.4 48.6c-14.5 0-29.3-9.7-29.3-24.2 0-14.8 14.8-24.4 29.3-24.4 14.8 0 24.4 9.7 24.4 24.4 0 14.6-9.6 24.2-24.4 24.2zM563 319.4c0-77.9-77.9-141.3-165.4-141.3-92.7 0-165.4 63.4-165.4 141.3S305 460.7 397.6 460.7c19.3 0 38.9-5.1 58.6-9.9l53.4 29.3-14.8-48.6C534 402.1 563 363.2 563 319.4zm-219.1-24.5c-9.7 0-19.3-9.7-19.3-19.6 0-9.7 9.7-19.3 19.3-19.3 14.8 0 24.4 9.7 24.4 19.3 0 10-9.7 19.6-24.4 19.6zm107.1 0c-9.7 0-19.3-9.7-19.3-19.6 0-9.7 9.7-19.3 19.3-19.3 14.5 0 24.4 9.7 24.4 19.3.1 10-9.9 19.6-24.4 19.6z"></path></svg></span></a><a class="reward-button alipay"><span><svg aria-hidden="true" focusable="false" data-prefix="fab" data-icon="alipay" class="svg-inline--fa fa-alipay fa-w-14 icon" role="img" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 448 512"><path fill="currentColor" d="M377.74 32H70.26C31.41 32 0 63.41 0 102.26v307.48C0 448.59 31.41 480 70.26 480h307.48c38.52 0 69.76-31.08 70.26-69.6-45.96-25.62-110.59-60.34-171.6-88.44-32.07 43.97-84.14 81-148.62 81-70.59 0-93.73-45.3-97.04-76.37-3.97-39.01 14.88-81.5 99.52-81.5 35.38 0 79.35 10.25 127.13 24.96 16.53-30.09 26.45-60.34 26.45-60.34h-178.2v-16.7h92.08v-31.24H88.28v-19.01h109.44V92.34h50.92v50.42h109.44v19.01H248.63v31.24h88.77s-15.21 46.62-38.35 90.92c48.93 16.7 100.01 36.04 148.62 52.74V102.26C447.83 63.57 416.43 32 377.74 32zM47.28 322.95c.99 20.17 10.25 53.73 69.93 53.73 52.07 0 92.58-39.68 117.87-72.9-44.63-18.68-84.48-31.41-109.44-31.41-67.45 0-79.35 33.06-78.36 50.58z"></path></svg></span></a><a class="reward-button paypal"><span><svg aria-hidden="true" focusable="false" data-prefix="fab" data-icon="paypal" class="svg-inline--fa fa-paypal fa-w-12 icon" role="img" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 384 512"><path fill="currentColor" d="M111.4 295.9c-3.5 19.2-17.4 108.7-21.5 134-.3 1.8-1 2.5-3 2.5H12.3c-7.6 0-13.1-6.6-12.1-13.9L58.8 46.6c1.5-9.6 10.1-16.9 20-16.9 152.3 0 165.1-3.7 204 11.4 60.1 23.3 65.6 79.5 44 140.3-21.5 62.6-72.5 89.5-140.1 90.3-43.4.7-69.5-7-75.3 24.2zM357.1 152c-1.8-1.3-2.5-1.8-3 1.3-2 11.4-5.1 22.5-8.8 33.6-39.9 113.8-150.5 103.9-204.5 103.9-6.1 0-10.1 3.3-10.9 9.4-22.6 140.4-27.1 169.7-27.1 169.7-1 7.1 3.5 12.9 10.6 12.9h63.5c8.6 0 15.7-6.3 17.4-14.9.7-5.4-1.1 6.1 14.4-91.3 4.6-22 14.3-19.7 29.3-19.7 71 0 126.4-28.8 142.9-112.3 6.5-34.8 4.6-71.4-23.8-92.6z"></path></svg></span></a><a class="reward-button kofi"><span><svg aria-hidden="true" focusable="false" data-prefix="fa" data-icon="coffee" class="svg-inline--fa fa-coffee fa-w-20 icon" role="img" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 640 512"><path fill="currentColor" d="M192 384h192c53 0 96-43 96-96h32c70.6 0 128-57.4 128-128S582.6 32 512 32H120c-13.3 0-24 10.7-24 24v232c0 53 43 96 96 96zM512 96c35.3 0 64 28.7 64 64s-28.7 64-64 64h-32V96h32zm47.7 384H48.3c-47.6 0-61-64-36-64h583.3c25 0 11.8 64-35.9 64z"></path></svg></span></a></div></div>    

    
    <footer class="post-footer">
      
    <div class="post-nav">
      <div class="post-nav-item">
    <a href="/articles/beechnut/theme-show-case/" rel="prev" title="Theme Show Case">
      <svg aria-hidden="true" focusable="false" data-prefix="fa" data-icon="chevron-left" class="svg-inline--fa fa-chevron-left fa-w-10 icon" role="img" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 320 512"><path fill="currentColor" d="M34.52 239.03L228.87 44.69c9.37-9.37 24.57-9.37 33.94 0l22.67 22.67c9.36 9.36 9.37 24.52.04 33.9L131.49 256l154.02 154.75c9.34 9.38 9.32 24.54-.04 33.9l-22.67 22.67c-9.37 9.37-24.57 9.37-33.94 0L34.52 272.97c-9.37-9.37-9.37-24.57 0-33.94z"></path></svg>
      Theme Show Case
    </a></div>
      <div class="post-nav-item">
    <a href="/articles/beechnut/hexo-next-injects/" rel="next" title="Hexo NexT 高阶教程之 Injects">
      Hexo NexT 高阶教程之 In...
      <svg aria-hidden="true" focusable="false" data-prefix="fa" data-icon="chevron-right" class="svg-inline--fa fa-chevron-right fa-w-10 icon" role="img" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 320 512"><path fill="currentColor" d="M285.476 272.971L91.132 467.314c-9.373 9.373-24.569 9.373-33.941 0l-22.667-22.667c-9.357-9.357-9.375-24.522-.04-33.901L188.505 256 34.484 101.255c-9.335-9.379-9.317-24.544.04-33.901l22.667-22.667c9.373-9.373 24.569-9.373 33.941 0L285.475 239.03c9.373 9.372 9.373 24.568.001 33.941z"></path></svg>
    </a></div>
    </div>    </footer>
    
  </div>
</article>
</div>
        </div>
        




<div class="card comment-body disqus active">
<div id="disqus_thread"></div>
<script>
var disqus_config = function () {
this.page.url = 'https://jiangtj.com/articles/beechnut/spring-cloud-gateway/';;  // Replace PAGE_URL with your page's canonical URL variable
this.page.identifier = 'articles/beechnut/spring-cloud-gateway/'; // Replace PAGE_IDENTIFIER with your page's unique identifier variable
};
(function() { // DON'T EDIT BELOW THIS LINE
var d = document, s = d.createElement('script');
s.src = 'https://jiangtj.disqus.com/embed.js';
s.setAttribute('data-timestamp', +new Date());
(d.head || d.body).appendChild(s);
})();
</script>
<noscript>Please enable JavaScript to view the <a target="_blank" rel="noopener" href="https://disqus.com/?ref_noscript">comments powered by Disqus.</a></noscript>
    </div>



      </main>

      <footer class="footer">
        <div>
          <div class="copyright">
  
  &copy; 
  <span>2021</span>
  <span class="with-love">
    <svg aria-hidden="true" focusable="false" data-prefix="fa" data-icon="heart" class="svg-inline--fa fa-heart fa-w-16 icon" role="img" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512"><path fill="currentColor" d="M462.3 62.6C407.5 15.9 326 24.3 275.7 76.2L256 96.5l-19.7-20.3C186.1 24.3 104.5 15.9 49.7 62.6c-62.8 53.6-66.1 149.8-9.9 207.9l193.5 199.8c12.5 12.9 32.8 12.9 45.3 0l193.5-199.8c56.3-58.1 53-154.3-9.8-207.9z"></path></svg>
    <i class="[object Object]"></i>
  </span>
  <span class="author">Mr.J</span>
</div>

<div class="powered-by">
  由 <a href="https://hexo.io/" class="theme-link" rel="noopener" target="_blank">Hexo</a> 强力驱动 v5.4.0
</div>

<span class="post-meta-divider">|</span>

<div class="theme-info">
  主题 – <a href="https://github.com/JiangTJ/hexo-theme-cake" class="theme-link" rel="noopener" target="_blank">Cake</a> v4.0.0
</div>

        </div>
      </footer>

    </div>

  </div>

  <div class="search-popup">
  <div class="search-header">
    <span class="search-icon"><svg aria-hidden="true" focusable="false" data-prefix="fas" data-icon="search" class="svg-inline--fa fa-search fa-w-16 icon" role="img" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512"><path fill="currentColor" d="M505 442.7L405.3 343c-4.5-4.5-10.6-7-17-7H372c27.6-35.3 44-79.7 44-128C416 93.1 322.9 0 208 0S0 93.1 0 208s93.1 208 208 208c48.3 0 92.7-16.4 128-44v16.3c0 6.4 2.5 12.5 7 17l99.7 99.7c9.4 9.4 24.6 9.4 33.9 0l28.3-28.3c9.4-9.4 9.4-24.6.1-34zM208 336c-70.7 0-128-57.2-128-128 0-70.7 57.2-128 128-128 70.7 0 128 57.2 128 128 0 70.7-57.2 128-128 128z"></path></svg></span>
    <div class="search-input-container">
      <input autocomplete="off" autocorrect="off" autocapitalize="none" placeholder="搜索..."
        spellcheck="false" type="text" id="search-input">
    </div>
    <span class="popup-btn-close"><svg aria-hidden="true" focusable="false" data-prefix="fas" data-icon="times-circle" class="svg-inline--fa fa-times-circle fa-w-16 icon" role="img" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512"><path fill="currentColor" d="M256 8C119 8 8 119 8 256s111 248 248 248 248-111 248-248S393 8 256 8zm121.6 313.1c4.7 4.7 4.7 12.3 0 17L338 377.6c-4.7 4.7-12.3 4.7-17 0L256 312l-65.1 65.6c-4.7 4.7-12.3 4.7-17 0L134.4 338c-4.7-4.7-4.7-12.3 0-17l65.6-65-65.6-65.1c-4.7-4.7-4.7-12.3 0-17l39.6-39.6c4.7-4.7 12.3-4.7 17 0l65 65.7 65.1-65.6c4.7-4.7 12.3-4.7 17 0l39.6 39.6c4.7 4.7 4.7 12.3 0 17L312 256l65.6 65.1z"></path></svg></span>
  </div>
  <div id="search-result"></div>
</div>
<div class="search-pop-overlay"></div>
<script id="ribbon" size="300" alpha="0.6" zIndex="-1" src="//cdn.jsdelivr.net/gh/theme-next/theme-next-canvas-ribbon@1/canvas-ribbon.js"></script><script>
((window.gitter = {}).chat = {}).options = {
  room: 'jiangtj/Lobby',
  activationElement: false
};
</script><script src="https://sidecar.gitter.im/dist/sidecar.v1.js" async defer></script><script>
  (function () {
    var bp = document.createElement('script');
    var curProtocol = window.location.protocol.split(':')[0];
    bp.src = (curProtocol === 'https') ? 'https://zz.bdstatic.com/linksubmit/push.js' : 'http://push.zhanzhang.baidu.com/push.js';
    var s = document.getElementsByTagName("script")[0];
    s.parentNode.insertBefore(bp, s);
  })();
</script>
<div class="moon-menu">
  <div class="moon-menu-items">
    
    <div id="moon-menu-item-back2bottom" class="moon-menu-item">
      <svg aria-hidden="true" focusable="false" data-prefix="fas" data-icon="chevron-down" class="svg-inline--fa fa-chevron-down fa-w-14 icon" role="img" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 448 512"><path fill="currentColor" d="M207.029 381.476L12.686 187.132c-9.373-9.373-9.373-24.569 0-33.941l22.667-22.667c9.357-9.357 24.522-9.375 33.901-.04L224 284.505l154.745-154.021c9.379-9.335 24.544-9.317 33.901.04l22.667 22.667c9.373 9.373 9.373 24.569 0 33.941L240.971 381.476c-9.373 9.372-24.569 9.372-33.942 0z"></path></svg>    </div>
    
    <div id="moon-menu-item-back2top" class="moon-menu-item">
      <svg aria-hidden="true" focusable="false" data-prefix="fas" data-icon="chevron-up" class="svg-inline--fa fa-chevron-up fa-w-14 icon" role="img" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 448 512"><path fill="currentColor" d="M240.971 130.524l194.343 194.343c9.373 9.373 9.373 24.569 0 33.941l-22.667 22.667c-9.357 9.357-24.522 9.375-33.901.04L224 227.495 69.255 381.516c-9.379 9.335-24.544 9.317-33.901-.04l-22.667-22.667c-9.373-9.373-9.373-24.569 0-33.941L207.03 130.525c9.372-9.373 24.568-9.373 33.941-.001z"></path></svg>    </div>
    
    <div id="moon-menu-item-chat" class="moon-menu-item">
      <svg aria-hidden="true" focusable="false" data-prefix="fa" data-icon="comments" class="svg-inline--fa fa-comments fa-w-18 icon" role="img" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512"><path fill="currentColor" d="M416 192c0-88.4-93.1-160-208-160S0 103.6 0 192c0 34.3 14.1 65.9 38 92-13.4 30.2-35.5 54.2-35.8 54.5-2.2 2.3-2.8 5.7-1.5 8.7S4.8 352 8 352c36.6 0 66.9-12.3 88.7-25 32.2 15.7 70.3 25 111.3 25 114.9 0 208-71.6 208-160zm122 220c23.9-26 38-57.7 38-92 0-66.9-53.5-124.2-129.3-148.1.9 6.6 1.3 13.3 1.3 20.1 0 105.9-107.7 192-240 192-10.8 0-21.3-.8-31.7-1.9C207.8 439.6 281.8 480 368 480c41 0 79.1-9.2 111.3-25 21.8 12.7 52.1 25 88.7 25 3.2 0 6.1-1.9 7.3-4.8 1.3-2.9.7-6.3-1.5-8.7-.3-.3-22.4-24.2-35.8-54.5z"></path></svg>    </div>
    
    <div id="moon-menu-item-switch_color" class="moon-menu-item">
      <svg aria-hidden="true" focusable="false" data-prefix="fas" data-icon="adjust" class="svg-inline--fa fa-adjust fa-w-16 icon" role="img" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512"><path fill="currentColor" d="M8 256c0 136.966 111.033 248 248 248s248-111.034 248-248S392.966 8 256 8 8 119.033 8 256zm248 184V72c101.705 0 184 82.311 184 184 0 101.705-82.311 184-184 184z"></path></svg>    </div>
    
  </div>
  <div class="moon-menu-button">
    <svg class="moon-menu-bg">
      <circle class="moon-menu-cricle" cx="50%" cy="50%" r="44%"></circle>
      <circle class="moon-menu-border" cx="50%" cy="50%" r="48%"></circle>
    </svg>
    <div class="moon-menu-content">
      <div class="moon-menu-icon"><svg aria-hidden="true" focusable="false" data-prefix="fas" data-icon="ellipsis-v" class="svg-inline--fa fa-ellipsis-v fa-w-6 icon" role="img" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 192 512"><path fill="currentColor" d="M96 184c39.8 0 72 32.2 72 72s-32.2 72-72 72-72-32.2-72-72 32.2-72 72-72zM24 80c0 39.8 32.2 72 72 72s72-32.2 72-72S135.8 8 96 8 24 40.2 24 80zm0 352c0 39.8 32.2 72 72 72s72-32.2 72-72-32.2-72-72-72-72 32.2-72 72z"></path></svg></div>
      <div class="moon-menu-text"></div>
    </div>
  </div>
</div><script src="/js/injector.js"></script>
<!-- hexo injector body_end start --><script>
// Check that service workers are supported
if ('serviceWorker' in navigator) {
  // Use the window load event to keep the page load performant
  window.addEventListener('load', () => {
    navigator.serviceWorker.register('/sw.js');
  });
}
</script>
<!-- hexo injector body_end end --></body>

</html>
